/**
 * @file     json_helper.hpp
 * @author   WMQ
 * @date     2024/11/6
 * @brief    json序列化和反序列化
 */

#ifndef JSON_HELPER_HPP
#define JSON_HELPER_HPP

#include "static_reflect.h"
#include <string>
#include <rapidjson/document.h>
#include <rapidjson/writer.h>

#define DESERIALIZE_SUCCESS           0
#define DESERIALIZE_FIND_MEMBER_ERROR 1
#define DESERIALIZE_GET_VALUE_ERROR   2
#define DESERIALIZE_JSON_TYPE_ERROR   3

namespace JsonHelper {

template <typename V>
inline constexpr int32_t GetValue(V &value, const rapidjson::Value &json_value)
{
    int32_t get_value_flag = false;
    if constexpr (std::is_same<bool, V>::value)
    {
        if (json_value.IsBool())
        {
            value = json_value.GetBool();
            get_value_flag = true;
        }
    }
    else if constexpr (std::is_same<std::string, V>::value)
    {
        if (json_value.IsString())
        {
            value = json_value.GetString();
            get_value_flag = true;
        }
    }
    else if constexpr (std::is_array<V>::value && (1 == std::rank<V>::value) &&
                       std::is_same<typename std::remove_all_extents<V>::type, char>::value)
    {
        if (json_value.IsString())
        {
            const int32_t char_len = std::extent<V>::value - 1 > json_value.GetStringLength()
                                         ? json_value.GetStringLength()
                                         : std::extent<V>::value - 1;
            strncpy(static_cast<char*>(value), json_value.GetString(), char_len);
            value[char_len] = '\0';
            get_value_flag = true;
        }
    }
    else if constexpr (std::is_same<int32_t, V>::value)
    {
        if (json_value.IsInt())
        {
            value = json_value.GetInt();
            get_value_flag = true;
        }
    }
    else if constexpr (std::is_same<uint32_t, V>::value)
    {
        if (json_value.IsUint())
        {
            value = json_value.GetUint();
            get_value_flag = true;
        }
    }
    else if constexpr (std::is_same<uint64_t, V>::value)
    {
        if (json_value.IsUint64())
        {
            value = json_value.GetUint64();
            get_value_flag = true;
        }
    }
    else if constexpr (std::is_same<int64_t, V>::value)
    {
        if (json_value.IsInt64())
        {
            value = json_value.GetInt64();
            get_value_flag = true;
        }
    }
    else if constexpr (std::is_same<float, V>::value)
    {
        if (json_value.IsFloat())
        {
            value = json_value.GetFloat();
            get_value_flag = true;
        }
    }
    else if constexpr (std::is_same<double, V>::value)
    {
        if (json_value.IsDouble())
        {
            value = json_value.GetDouble();
            get_value_flag = true;
        }
    }
    else if constexpr (std::is_enum<V>::value)
    {
        if (json_value.IsInt())
        {
            value = static_cast<V>(json_value.GetInt());
            get_value_flag = true;
        }
    }
    return get_value_flag;
}

template <typename V>
inline void Deserialize(const rapidjson::Value &json_value, const char* key_name, V &value,
                        int32_t &error_code, std::string &error_info)
{
    if (DESERIALIZE_SUCCESS != error_code)
    {
        return;
    }
    if constexpr (StaticReflect::IsReflected<V>)
    {
        StaticReflect::ForEach(value, [&json_value, &error_code,
                                       &error_info](const char* field_name, auto &&field_value) {
            if (DESERIALIZE_SUCCESS != error_code)
            {
                return;
            }
            if (!json_value.IsObject())
            {
                error_code = DESERIALIZE_JSON_TYPE_ERROR;
                error_info.append(field_name);
                return;
            }
            const rapidjson::Value::ConstMemberIterator member_itr =
                json_value.FindMember(field_name);
            if (member_itr == json_value.MemberEnd())
            {
                error_code = DESERIALIZE_FIND_MEMBER_ERROR;
                error_info.append(field_name);
                return;
            }
            Deserialize(member_itr->value, field_name, field_value, error_code, error_info);
        });
    }
    else if constexpr (std::is_array<V>::value &&
                       !(std::is_array<V>::value && (1 == std::rank<V>::value) &&
                         std::is_same<typename std::remove_all_extents<V>::type, char>::value))
    {
        if (!json_value.IsArray())
        {
            error_code = DESERIALIZE_JSON_TYPE_ERROR;
            error_info.append(key_name);
            return;
        }
        const int32_t array_len =
            std::extent<V>::value > json_value.Size() ? json_value.Size() : std::extent<V>::value;
        for (int i = 0; i < array_len; ++i)
        {
            const rapidjson::Value &array_value = json_value[i];
            Deserialize(array_value, key_name, value[i], error_code, error_info);
        }
    }
    else
    {
        if (!GetValue(value, json_value))
        {
            error_code = DESERIALIZE_GET_VALUE_ERROR;
            error_info.append(key_name);
        }
    }
}

template <typename V, typename std::enable_if<StaticReflect::IsReflected<V>, int>::type = 0>
inline void DeserializeStruct(const rapidjson::Value &json_value, V &value, int32_t &error_code,
                              std::string &error_info)
{
    Deserialize(json_value, "", value, error_code, error_info);
}

template <typename V>
inline constexpr void WriteValue(rapidjson::Writer<rapidjson::StringBuffer> &writer, V &value)
{
    if constexpr (std::is_same_v<V, bool>)
    {
        writer.Bool(value);
    }
    else if constexpr (std::is_same_v<V, std::string>)
    {
        writer.String(value.c_str());
    }
    else if constexpr (std::is_array<V>::value && (1 == std::rank<V>::value) &&
                       std::is_same_v<typename std::remove_all_extents<V>::type, char>)
    {
        writer.String(static_cast<char*>(value));
    }
    else if constexpr (std::is_same_v<V, int32_t>)
    {
        writer.Int(value);
    }
    else if constexpr (std::is_same_v<V, uint32_t>)
    {
        writer.Uint(value);
    }
    else if constexpr (std::is_same_v<V, uint64_t>)
    {
        writer.Uint64(value);
    }
    else if constexpr (std::is_same_v<V, int64_t>)
    {
        writer.Int64(value);
    }
    else if constexpr (std::is_same_v<V, float> || std::is_same_v<V, double>)
    {
        writer.Double(value);
    }
    else if constexpr (std::is_enum<V>::value)
    {
        writer.Int64(static_cast<int64_t>(value));
    }
    else
    {
        writer.Null();
    }
}

template <typename V>
inline void Serialize(rapidjson::Writer<rapidjson::StringBuffer> &writer, const char* key_name,
                      V &value)
{
    if constexpr (StaticReflect::IsReflected<V>)
    {
        writer.StartObject();
        StaticReflect::ForEach(value, [&writer](const char* field_name, auto &&field_value) {
            writer.Key(field_name);
            Serialize(writer, field_name, field_value);
        });
        writer.EndObject();
    }
    else if constexpr (std::is_array<V>::value &&
                       !(std::is_array<V>::value && (1 == std::rank<V>::value) &&
                         std::is_same_v<typename std::remove_all_extents<V>::type, char>))
    {
        writer.StartArray();
        const int32_t array_len = std::extent<V>::value;
        for (int i = 0; i < array_len; ++i)
        {
            Serialize(writer, key_name, value[i]);
        }
        writer.EndArray();
    }
    else
    {
        WriteValue(writer, value);
    }
}

template <typename V, typename std::enable_if<StaticReflect::IsReflected<V>, int>::type = 0>
inline void SerializeStruct(rapidjson::Writer<rapidjson::StringBuffer> &writer, V &value)
{
    Serialize(writer, "", value);
}

} // namespace JsonHelper

#endif